iT邦幫忙

2021 iThome 鐵人賽

DAY 24
1

Domain Driven Design 是一個最近開始在台灣紅起來的一種設計以及開發方式,他的出現主要是為了解決大型專案中複雜的領域問題,透過與領域專家對話,交換意見,最終歸納出一個專案成員都能理解的共通語言,而這共通語言將不止用來跟團隊成員溝通,也會出現在規格文件上,甚至連程式碼也是用同樣的語言建構出來的。

在學習 Domain Driven Design 的過程中會學到一大堆新名詞,如果一股腦的要把這些名詞一次學起來並馬上套用在專案上,應該很容易就迷失了方向,像是一定要為 Value Object 還有 Entity 做歸類啦,還有學到 Bounded Context 跟 Subdomain 之後就要馬上拆 module ,這些都有可能不是第一件需要做的事。那除了這些之外, Domain Driven Design 還可以為我們帶來什麼呢?對一個 Android 應用程式來說 Domain Driven Design 可以怎麼“落地”呢?為了回答這些問題,先從我在 Domain Driven Design 學到的觀念以及心得開始吧!

模型

因為最近有很多朋友都有在看房子,我也在旁邊練功吸趴,看了之後才發現平面圖裡面是有很多學問的,同時也發現每個不同建案的平面圖也都是千奇百怪,有的只畫出傢俱跟隔間,但是沒有附上比例尺,所以搞不好到時床放進去的時候所能運用的空間比平面圖還小。有的平面圖還有鮮豔的顏色,比枯燥乏味的黑白線條還有吸引力。但不管平面怎麼畫,實際上住進去的空間還是有很多“細節”是無法從平面圖上感受出來的,需要實際走訪,規劃裝潢傢俱,或是住上一個禮拜才能感受得到。

這個平面圖就是一種“模型”,這個模型有他存在的目的,是一個經過“抽象化”過的概念,這個模型在房子還沒真正蓋起來時很好用,銷售人員使用這個模型來跟客戶介紹房子的格局,一張紙做的平面圖,比完全用嘴巴解說來的容易溝通,也比一個完整的 3D 模型花費的成本更低,在這時候,銷售人員跟客戶可以很容易的使用“共通語言”來進行溝通,比起說出“那個角落”、或是“這個長度多少”,在紙上直接指出“陽台的這個角落”,還有用筆畫出“廚房這個邊的長度”,在溝通上是容易多了。

在專案開發中,我們也可以定義出一個經過“抽象化”過的模型來輔助開發,這個模型不限定於是一個 Data class 或是 Object ,而可以是ㄧ組經過精煉的 class 還有他們的交互行為所形成的概念,他們會是一個具有“高聚合”性質的群體。

Domain

Domain 這個詞在這個系列文中已經出現了好幾次了,他不是一個很好懂的概念,但是這個詞很常在架構的討論上出現,你可以把 Domain 解讀為商業邏輯,或是整個應用程式的核心。如果還是不懂,可以參考看看 Domain-Driven Design 這本書原作者所寫的定義:每個軟體程式是為了執行使用者的某個活動,或是滿足使用者個某個需求,這些使用者應用軟體的區域就是軟體的領域(Domain)。所以如果以便利貼 App 為例,任何對於便利貼的操作、編輯行為就是這個 App 的 Domain。

Ubiquitous Language

這個是“共通語言”的意思,有一個好的共通語言可以加速專案的開發。舉一個大家應該都能深刻體驗的例子說明好了:有一天,測試人員發現一個問題,他說 App 操作到一半就 Freeze 了,就回報給 PM ,然後這時候呢,PM 用即時通訊軟體將這問題再跟工程師說有發現這問題,這時候工程師就試著用自己的手機重現這個問題,結果試了老半天試不出問題,就跟 PM 說這 bug 無法重現。後來呢,PM 就親自去找測試人員,要測試人員操作給他看,結果發現了測試人員說的沒錯,果然有問題!PM 於是又去跟工程師說,你這樣很不專業,明明這麼簡單就能夠重現問題了你怎麼說沒有,工程師不服氣,叫 PM 操作一次給他看,這個 PM 後來就在某個頁面上一直按某個按鈕,結果還真的都沒反應,於是工程師搶過手機自己操作,結果可以動了!你們猜發生什麼事?結果是按鈕太小太難按到,是觸控區域的問題!

為什麼會浪費這麼多時間在來回溝通?其中一個原因是因為測試人員的 Freeze 跟工程師對於 Freeze 的定義天差地遠!測試人員覺得按了一個按鈕沒反應就叫做 Freeze ,但是工程師覺得 App 完全卡死到有 ANR(Android not responding) 出現才叫做 Freeze,工程師在操作 App 時發現系統 Back 鍵還可以按,點擊其他區域也都有反應時,就會下意識地覺得這不是 Freeze 的狀態,所以沒有 “Freeze” 的問題。

要解決這問題也很簡單,只要團隊定義好“共通語言”就行了,像是下面這樣:

  1. Freeze - App 的所有按鈕都沒有回饋,也沒有動畫在跑,按 back 鍵也沒有反應。
  2. Laggy - App 要等 1-2 秒後 UI 才看到反應,但不會到完全卡死。
  3. No respond - 點擊按鈕之後沒有預期的頁面跳轉或是狀態改變,但是其他在同一個頁面上的按鈕是可以操作的。

如果沒有共通語言的話,在專案開發上將會有很大的成本用來溝通以及翻譯,PM 跟設計師討論 UI UX 時就要在設計稿上進行圖像到語言的翻譯,之後 PM 在寫需求文件時可能又將這概念翻譯成了另外一個字,後來工程師依據設計稿寫程式時,xml 是一個名字,ViewModel 又是另一個名字,等到畫面做完要接 Server API 時,發現 Server 因為 DB 欄位的名字已經取好了,App 在接資料時也懶得做更換,也依照 Server 的命名來接資料。於是就出現了一個怪異的現象,同一份專案的程式碼,明明是同一個概念,卻有三個不一樣的名字,最要不得的,這三個名字還沒有一個可以跟 PM 在規格上寫內容對的上!當 PM 回報 Bug 時,工程師們就要當人體翻譯機,要想辦法找出相對應的 Class 的底在哪裡...

從對話中提取模型

對於任何類型的應用程式來說,上面所說的這三個觀念都是受用無窮的,就算你沒有要設計大型的應用程式也一樣很有幫助。下面就來做個情境模擬,現在有兩個工程師:阿明跟小美,他們都是便利貼專案的成員,而且剛學完 DDD 的基本概念,已經迫不及待的要馬上進行大改造了,剛好又遇到 PM 的新需求:要支援放大、縮小以及平移的功能,於是他們就定了一個會議時間要來好好討論因應這個新需求的變動:

阿明:誒你覺得我們要從哪先開始?

小美:我覺得可以先從定義模型開始,便利貼應該就是我們的核心模型對吧?

阿明:照理說應該是這樣沒錯,但是我發現一件事,你有沒有覺得這整個畫面叫做 EditorViewModel 有點怪怪的?叫做編輯器這個名字好嗎?這看起來不太像是我平常接觸到的編輯器。

小美:什麼意思?哪裡不像呢?

阿明:我平常對於編輯器的第一印象就是 Android Studio 或是小畫家,功能非常齊全,而且不會跟別人合作。

小美:對誒,真說到重點了, Editor 這個字沒有顯現出“共同協作”的意圖,這樣我開始也覺得怪怪的了,那我們改名叫做 CoEditor 如何?

阿明:聽起來不錯,所以我們會在 CoEditor 進行援放大、縮小以及平移的操作,還可以選擇便利貼,對它進行其他操作。

小美:但是這個 CoEditor 擁有所有的便利貼嗎?超過視線範圍之外的便利貼也要一起顯示嗎?既然我們的 App 可以支援放大縮小,是不是可以新增一個概念來避免讓 CoEditor 擁有所有的便利貼?

阿明:視線範圍...啊,對了!那加入視圖(ViewPort) 這概念如何?對 ViewPort 做操作的話,可以看到的便利貼數量也跟著做改變很合理吧。

小美:所以你的意思是說 CoEditor 裡面還有另外一個類別叫做 ViewPort 嗎?會不會太多類別了呢?

阿明:你說的沒錯,不然這樣子好了,我們第一步先重構,還不要支援放大縮小跟平移,但是保留這個彈性,等需要時再加進去就好了。

小美:等等,我突然想到我們有一個 View 叫做 BoardView ,是不是應該要叫做 VewPortView 比較好?

阿明:所以我們要拋棄“白板”這個概念了嗎?把便利貼放在白板上看起來也是蠻合理的不是嗎?

小美:但是如果是白板的話...應該還要可以在上面畫畫,以後會想要新增畫畫的功能嗎?

阿明:目前沒有往這方向發展的規劃,好吧,看起來 VewPortView 這名字蠻合理的。另外還有其他的部分像是選單,我們在現在的架構中的 Domain 層還沒有這個概念,只有在 View 層有這概念,我覺得 Domain 層應該要能夠表達這點。

小美:那就新增一個 ContextMenu 的類別吧,這部分我也蠻同意的,這部分應該蠻好做的。

阿明:看看時間也差不多了,我等等來整理一下討論的內容寫成會議紀錄,明天找時間再接著討論吧!

從上面的對話中,新的關鍵字跟不同的解讀都慢慢冒了出來,因此而產生了更多更有意義的模型以及共通語言,從 Editor 、CoEditor、ViewPort 到 ContextMenu ,漸漸地理出彼此之間的交互關係以及職責,到這邊不知道你有沒有發現,其實目前專案程式碼中類別的名稱都還有改進的空間,而且 View 層跟 ViewModel 層的名稱到處都有不一致的現象,這對於未來的維護上會造成很大的問題。

小結

Domain Driven Design 是一個很大的主題,但並不代表我們要全部學完才能應用在自己的專案上,我很喜歡當中的模型與 Ubiquitous Language 的概念,這樣一來,我們可以在專案中從被動的角色,轉換成主動的角色。被動的部分是因為,從 PM 那邊接受新功能的需求,通常會直接使用工程的角度去實作需要的功能,而沒有完整理解該功能的存在是為了解決什麼問題,Context 是什麼。而至於主動的部分,學了 DDD 之後,可以主動與 PM 展開對話,從定義名詞開始,漸漸的把模糊的需求整理成有意義的模型,像剛剛的對話中就提煉出了 ViewPort 的這個新概念,以後使用 ViewPort 來跟 PM 與設計師溝通時,一定會比“手機畫面”這種對於領域知識無意義的名詞還要來的明確。


上一篇
Re-architect with UseCase driven design
下一篇
使用 Domain Driven Design 來進行架構設計
系列文
Jetpack Compose X Android Architecture X Functional Reactive Programming30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言